using Grpc.Core;
using CSharpAnalyzer.AnalyzerService.Grpc;
using CSharpAnalyzer.AnalyzerService.Workspace;
using CSharpAnalyzer.AnalyzerService.Serialization;

namespace CSharpAnalyzer.AnalyzerService.Services;

/// <summary>
/// gRPC service implementation for C# solution analysis
/// </summary>
public class AnalyzerServiceImpl : Grpc.AnalyzerService.AnalyzerServiceBase
{
    private readonly ILogger<AnalyzerServiceImpl> _logger;

    /// <summary>
    /// Initializes a new instance of the AnalyzerServiceImpl
    /// </summary>
    public AnalyzerServiceImpl(ILogger<AnalyzerServiceImpl> logger)
    {
        _logger = logger;
    }

    /// <summary>
    /// Analyzes a C# solution and returns syntax information
    /// </summary>
    public override async Task<SolutionResponse> AnalyzeSolution(
        SolutionRequest request,
        ServerCallContext context)
    {
        _logger.LogInformation("Analyzing solution: {SolutionPath}", request.SolutionPath);

        var response = new SolutionResponse();
        var errors = new List<string>();

        try
        {
            // Validate request
            if (string.IsNullOrWhiteSpace(request.SolutionPath))
            {
                errors.Add("Solution path is required");
                response.Errors.AddRange(errors);
                return response;
            }

            // Load solution
            using var loader = new WorkspaceLoader();
            Microsoft.CodeAnalysis.Solution solution;

            try
            {
                solution = await loader.LoadSolutionAsync(
                    request.SolutionPath,
                    context.CancellationToken);
            }
            catch (FileNotFoundException ex)
            {
                errors.Add($"Solution file not found: {ex.Message}");
                response.Errors.AddRange(errors);
                return response;
            }
            catch (Exception ex)
            {
                errors.Add($"Error loading solution: {ex.Message}");
                response.Errors.AddRange(errors);
                return response;
            }

            response.SolutionName = Path.GetFileNameWithoutExtension(solution.FilePath);

            // Get documents to analyze
            var documents = request.ProjectNames.Count > 0
                ? loader.GetDocumentsFromProjects(solution, request.ProjectNames)
                : loader.GetAllDocuments(solution);

            // Serialize syntax for each project
            var serializer = new SyntaxSerializer();
            var projectGroups = documents.GroupBy(d => d.Project.Name);

            foreach (var projectGroup in projectGroups)
            {
                var projectInfo = new ProjectInfo
                {
                    Name = projectGroup.Key,
                    Path = projectGroup.First().Project.FilePath ?? string.Empty
                };

                foreach (var document in projectGroup)
                {
                    try
                    {
                        var syntaxJson = await serializer.SerializeDocumentAsync(
                            document,
                            context.CancellationToken);

                        var documentInfo = new DocumentInfo
                        {
                            Name = document.Name,
                            Path = document.FilePath ?? string.Empty,
                            SyntaxJson = syntaxJson
                        };

                        projectInfo.Documents.Add(documentInfo);

                        _logger.LogDebug("Serialized document: {DocumentName}", document.Name);
                    }
                    catch (Exception ex)
                    {
                        var error = $"Error serializing document {document.Name}: {ex.Message}";
                        errors.Add(error);
                        _logger.LogError(ex, "Error serializing document: {DocumentName}", document.Name);
                    }
                }

                response.Projects.Add(projectInfo);
            }

            if (errors.Any())
            {
                response.Errors.AddRange(errors);
            }

            _logger.LogInformation(
                "Analysis complete. Projects: {ProjectCount}, Errors: {ErrorCount}",
                response.Projects.Count,
                errors.Count);

            return response;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unexpected error during solution analysis");
            errors.Add($"Unexpected error: {ex.Message}");
            response.Errors.AddRange(errors);
            return response;
        }
    }
}
